﻿using System;
using System.Collections.Generic;
using System.Web.UI;
using Microsoft.SharePoint.WebControls;

namespace GDExtensions
{
    /// <summary>
    /// Extension methods for generic controls
    /// </summary>
    /// <remarks></remarks>
    public static class ControlExtensions
    {
        #region Public Methods
        /// <summary>
        /// Method to find a control by an ID recursively. This method will try to find it in its
        /// child controls and parent's child controls prior to doing a recursive search.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="controlId">The control id to search for.</param>
        /// <returns>Control if found, else null</returns>
        /// <remarks></remarks>
        public static Control FindControlById(this Control control, string controlId)
        {
            // Call the private method
            return _FindControlById(control, controlId);
        }

        /// <summary>
        /// Method to find a control by an ID recursively.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="controlId">The control id to search for.</param>
        /// <returns>Control if found, else null</returns>
        /// <remarks></remarks>
        public static Control FindControlByIdRecursively(this Control control, string controlId)
        {
            // Call the private method
            return _FindControlByIdRecursively(control, controlId);
        }

        /// <summary>
        /// Method to find the form fields and labels recursively.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="fields">The fields.</param>
        /// <param name="labels">The labels.</param>
        /// <remarks></remarks>
        public static void FindFieldControls(this Control control, ref Dictionary<string, FormField> fields,
            ref Dictionary<string, FieldLabel> labels)
        {
            // Call the private method
            _FindFieldControls(control, ref fields, ref labels);
        }

        /// <summary>
        /// Method to remove the links for a lookup field.
        /// </summary>
        /// <param name="fields">The fields.</param>
        /// <param name="fieldName">Name of the field.</param>
        public static void RemoveLinksFromLookup(this Dictionary<string, FormField> fields, string fieldName)
        {
            // Remove the links
            _RemoveLinksFromLookup(fields, fieldName);
        }

        /// <summary>
        /// Method to set the control mode of a field.
        /// </summary>
        /// <param name="fields">The fields.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="controlMode">The control mode.</param>
        public static void SetControlMode(this Dictionary<string, FormField> fields, string fieldName, SPControlMode controlMode)
        {
            // Call the private method
            _SetControlMode(fields, fieldName, controlMode);
        }

        /// <summary>
        /// Sets the name of the template.
        /// </summary>
        /// <param name="fields">The fields.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="templateName">Name of the template.</param>
        /// <remarks></remarks>
        public static void SetTemplateName(this Dictionary<string, FormField> fields, string fieldName, string templateName)
        {
            // Call the private method
            _SetTemplateName(fields, fieldName, templateName);
        }

        /// <summary>
        /// Sets the name of the template.
        /// </summary>
        /// <param name="labels">The labels.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="templateName">Name of the template.</param>
        /// <remarks></remarks>
        public static void SetTemplateName(this Dictionary<string, FieldLabel> labels, string fieldName, string templateName)
        {
            // Call the private method
            _SetTemplateName(labels, fieldName, templateName);
        }

        /// <summary>
        /// Method to update the field labels to match the form field's control mode.
        /// </summary>
        /// <param name="labels">The labels.</param>
        /// <param name="fields">The fields.</param>
        /// <remarks></remarks>
        public static void UpdateFieldLabels(this Dictionary<string, FieldLabel> labels, Dictionary<string, FormField> fields)
        {
            // Call the private method
            _UpdateFieldLabels(labels, fields);
        }

        /// <summary>
        /// Updates the read only fields to be read-only.
        /// </summary>
        /// <param name="fields">The fields.</param>
        /// <remarks></remarks>
        public static void UpdateReadOnlyFields(this Dictionary<string, FormField> fields)
        {
            // Call the private method
            _UpdateReadOnlyFields(fields);
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Method to find a control by an ID recursively. This method will try to find it in its
        /// child controls and parent's child controls prior to doing a recursive search.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="controlId">The control id to search for.</param>
        /// <returns>Control if found, else null</returns>
        /// <remarks></remarks>
        private static Control _FindControlById(Control control, string controlId)
        {
            Control controlToFind = default(Control);

            // Ensure the control is not null
            if (control != null)
            {
                // Try to find the control
                controlToFind = control.FindControl(controlId);

                // See if wasn't found, and a parent exists
                if (controlToFind == null && control.Parent != null)
                {
                    // Try to find it in the parent object
                    controlToFind = control.Parent.FindControl(controlId);
                }

                // See if it still wasn't found
                if (controlToFind == null)
                {
                    // Get the root control
                    while (control.Parent != null)
                    {
                        control = control.Parent;
                    }

                    // Search the root control recursively for the control id
                    controlToFind = control.FindControlByIdRecursively(controlId);
                }
            }

            return controlToFind;
        }

        /// <summary>
        /// Method to find a control by an ID recursively.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="controlId">The control id to search for.</param>
        /// <returns>Control if found, else null</returns>
        /// <remarks></remarks>
        private static Control _FindControlByIdRecursively(Control control, string controlId)
        {
            // Ensure the control is not null
            if (control != null)
            {
                // See if the control id matches this control
                if (controlId.Equals(control.ID, StringComparison.CurrentCultureIgnoreCase))
                {
                    // Return it
                    return control;
                }

                // See if the control has child controls
                if (control.Controls != null && control.Controls.Count > 0)
                {
                    // Parse the child controls
                    foreach (Control childControl in control.Controls)
                    {
                        // find the control
                        Control controlToFind = childControl.FindControlByIdRecursively(controlId);

                        // See if it was found
                        if (controlToFind != null)
                        {
                            // Return it
                            return controlToFind;
                        }
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Method to find the form fields and labels recursively.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <param name="fields">The fields.</param>
        /// <param name="labels">The labels.</param>
        /// <remarks></remarks>
        private static void _FindFieldControls(Control control, ref Dictionary<string, FormField> fields,
            ref Dictionary<string, FieldLabel> labels)
        {
            // Add the form field to the fields collection
            if (control is FormField)
            {
                FormField field = control as FormField;
                fields.Add(field.Field.StaticName, field);
            }
            // Add the field label to the fields collection
            else if (control is FieldLabel)
            {
                FieldLabel field = control as FieldLabel;
                labels.Add(field.Field.StaticName, field);
            }
            // See if the control has child controls
            else if (control.Controls != null && control.Controls.Count > 0)
            {
                // Parse the child controls
                foreach (Control child in control.Controls)
                {
                    // Search the child controls recursively
                    child.FindFieldControls(ref fields, ref labels);
                }
            }
        }

        /// <summary>
        /// Method to remove the links for a lookup field.
        /// </summary>
        /// <param name="fields">The fields.</param>
        /// <param name="fieldName">Name of the field.</param>
        private static void _RemoveLinksFromLookup(Dictionary<string, FormField> fields, string fieldName)
        {
            // Ensure the field exists
            if (fields.ContainsKey(fieldName))
            {
                FormField formField = fields[fieldName];

                // Ensure the item exists
                if (formField.Item == null) { return; }

                // Ensure this is a lookup field
                if (formField.Field.Type != Microsoft.SharePoint.SPFieldType.Lookup) { return; }

                // Get the field value as text
                string fieldValue = formField.Field.GetFieldValueAsText(formField.Item[formField.FieldName] + "");

                // Clear the controls and add the field value
                formField.Controls.Clear();
                formField.Controls.Add(new LiteralControl(fieldValue));
            }
        }

        /// <summary>
        /// Method to set the control mode of a field.
        /// </summary>
        /// <param name="fields">The fields.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="controlMode">The control mode.</param>
        private static void _SetControlMode(Dictionary<string, FormField> fields, string fieldName, SPControlMode controlMode)
        {
            // Ensure the field exists
            if (fields.ContainsKey(fieldName))
            {
                // Set the control mode
                fields[fieldName].ControlMode = controlMode;
            }
        }

        /// <summary>
        /// Sets the name of the template.
        /// </summary>
        /// <param name="fields">The fields.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="templateName">Name of the template.</param>
        /// <remarks></remarks>
        private static void _SetTemplateName(Dictionary<string, FormField> fields, string fieldName, string templateName)
        {
            // Ensure the field name exists
            if (fields.ContainsKey(fieldName))
            {
                // Check the control mode of the field
                if (fields[fieldName].ControlMode == SPControlMode.Display)
                {
                    // Set the display name
                    fields[fieldName].DisplayTemplateName = templateName;
                }
                // Else, see if this field is required
                else
                {
                    // Set the alternate & template names
                    fields[fieldName].AlternateTemplateName = templateName;
                    fields[fieldName].TemplateName = templateName;
                }
            }
        }

        /// <summary>
        /// Sets the name of the template.
        /// </summary>
        /// <param name="labels">The labels.</param>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="templateName">Name of the template.</param>
        /// <remarks></remarks>
        private static void _SetTemplateName(Dictionary<string, FieldLabel> labels, string fieldName, string templateName)
        {
            // Ensure the field name exists
            if (labels.ContainsKey(fieldName))
            {
                // Check the control mode of the field
                if (labels[fieldName].ControlMode == SPControlMode.Display)
                {
                    // Set the display name
                    labels[fieldName].DisplayTemplateName = templateName;
                }
                // Else, see if this field is required
                else
                {
                    // Set the alternate & template names
                    labels[fieldName].AlternateTemplateName = templateName;
                    labels[fieldName].TemplateName = templateName;
                }
            }
        }

        /// <summary>
        /// Method to update the field labels to match the form field's control mode.
        /// </summary>
        /// <param name="labels">The labels.</param>
        /// <param name="fields">The fields.</param>
        /// <remarks></remarks>
        private static void _UpdateFieldLabels(Dictionary<string, FieldLabel> labels, Dictionary<string, FormField> fields)
        {
            // Parse the control fields
            foreach (FormField field in fields.Values)
            {
                // Ensure the label has this control
                if (labels.ContainsKey(field.FieldName))
                {
                    // Update the label's control mode
                    labels[field.FieldName].ControlMode = field.ControlMode;
                }
            }
        }

        /// <summary>
        /// Updates the read only fields to be read-only.
        /// </summary>
        /// <param name="fields">The fields.</param>
        /// <remarks></remarks>
        private static void _UpdateReadOnlyFields(Dictionary<string, FormField> fields)
        {
            // Parse the form fields
            foreach (FormField field in fields.Values)
            {
                // See if this is a read-only field
                if (field.Field.ReadOnlyField)
                {
                    // Set the control mode
                    field.ControlMode = SPControlMode.Display;
                }
            }
        }
        #endregion
    }
}
